/*  generalthread.cpp
    Created by Garrett Williams GoatbitsSoftware
    version: 020808
    Licence: GPL
 */

#include <sys/time.h>
#include <unistd.h>

#include "generalthread.hpp"

using namespace GeneralThread_NS;

// General Mutex.
GeneralMutex::GeneralMutex()
{
    pthread_mutex_init(&_mutex, NULL);
    return;
}

GeneralMutex::~GeneralMutex()
{
    destroy();
    return;
}

void 
GeneralMutex::lock()
{
    pthread_mutex_lock(&_mutex);
    return;
}

void 
GeneralMutex::unlock()
{
    pthread_mutex_unlock(&_mutex);
    return;
}

void 
GeneralMutex::destroy()
{
    pthread_mutex_destroy(&_mutex);
    return;
}

// general conditional
GeneralCondition::GeneralCondition()
{
    pthread_mutex_init(&_mutex, NULL);
    pthread_cond_init(&_condition,NULL);
    return;
}

GeneralCondition::~GeneralCondition()
{
    destroy();
    return;
}

// Cause thread to sleep for "duration" in seconds
int 
GeneralCondition::timeWait(unsigned duration)
{
    struct timeval  time_now;
    struct timespec time_out;

    gettimeofday(&time_now,NULL);                                 // get current time.
    time_out.tv_sec = time_now.tv_sec + duration;                       // set current time.
    time_out.tv_nsec = time_now.tv_usec * 1000; // time need to bein nano-seconds
    pthread_mutex_lock(&_mutex);
    int rc = pthread_cond_timedwait(&_condition,&_mutex,&time_out);
    pthread_mutex_unlock(&_mutex);
    return rc;
}

void 
GeneralCondition::condWait()
{
    pthread_mutex_lock(&_mutex);
    pthread_cond_wait(&_condition,&_mutex);
    pthread_mutex_unlock(&_mutex);
    return;
}

void 
GeneralCondition::condGo()
{
    pthread_cond_signal(&_condition);
    return;
}

void 
GeneralCondition::destroy()
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_condition);
    return;
}

// general thread.
GeneralThread::GeneralThread(GeneralMutex &gm)
    : _mutex(gm)
    , _running(false)
    , _stop_cond(false)
    , _lock_mutex(false)
    , _wait_cond(0)
{
}

GeneralThread::~GeneralThread()
{
}

int 
GeneralThread::create(void *pt)
{
    caller(pt);
    _thread_id = pthread_create(&_thread,NULL,GeneralThread::threadCall,pt);
    _running = true;
    return (int)_thread;
}

// static.
/*
    This is the main loop.  To interrupt it use the interfaces
    That set the condition variables.  the continue will work directly.
    The mutex is accessed directly from outside of this class, or using 
    lock and unlock methods.
 */
void* 
GeneralThread::threadCall(void *pt)
{
    GeneralThread *pt_thread = (GeneralThread*)pt;
    while(pt_thread->running())
    {
        if(pt_thread->stopCond())
        {
            pt_thread->condWait();
            pt_thread->stopCond(false);
        }
        if(pt_thread->waitCond())
        {
            // thread did not time out a change occured.  deal with it.
            if(pt_thread->timeWait(pt_thread->waitCond())) 
            {
                pt_thread->timeCompleted();
            }
            else
            {
                pt_thread->timeInterupted();
            }
            pt_thread->waitCond(0);
        }
        ((GeneralThread*)pt)->process(pt);
    }
    return pt;
}

// End the running thread.
void 
GeneralThread::stop()
{
    lock();
    _running = false;
    unlock();
    pthread_exit(NULL);
    return;
}

// lock the mutex
void 
GeneralThread::lock()
{
    _mutex.lock();
    return;
}

// unlock the mutex.
void 
GeneralThread::unlock()
{
    _mutex.unlock();
    return;
}

bool  
GeneralThread::running()
{
    return _running;
}

void  
GeneralThread::running(bool r)
{
    lock();
    _running=r;
    unlock();
    return;
}

bool  
GeneralThread::stopCond()
{
    return _stop_cond;
}

void  
GeneralThread::stopCond(bool s)
{
    lock();
    _stop_cond=s;
    if(!_stop_cond)
    {
        condGo();
        _wait_cond = 0;
    }
    unlock();
    return;
}

bool  
GeneralThread::lockMutex()
{
    return _lock_mutex;
}

void  
GeneralThread::lockMutex(bool m)
{
    lock();
    _lock_mutex=m;
    unlock();
    return;
}

unsigned  
GeneralThread::waitCond()
{
    return _wait_cond;
}

void      
GeneralThread::waitCond(unsigned w)
{
    lock();
    _wait_cond = w;
    if(!_wait_cond)
    {
        condGo();
        _stop_cond = 0;
    }
    unlock();
    return;
}

void* 
GeneralThread::caller() 
{
    return _caller;
}

void  
GeneralThread::caller(void* a)
{
    lock();
    _caller = a;
    unlock();
    return;
}


